/*
 * Copyright 2008-2023 dexian.vip. All rights reserved.
 * Support: http://www.dexian.vip
 * License: http://www.dexian.vip/license
 */

package vip.dexian.admin.security.dynamic;

import vip.dexian.admin.service.AdminService;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
import org.springframework.stereotype.Component;

import java.util.function.Supplier;

/**
 * 授权
 *
 * @author 挺好的 2023年06月06日 11:09
 */
@Component
@Slf4j
public class AuthorizationManagerAdapter implements AuthorizationManager <RequestAuthorizationContext> {

    /**
     * 权限配置
     */
    @Resource (name = "authorizationProperties")
    private AuthorizationProperties authorizationProperties;

    /**
     * 管理员
     */
    @Resource (name = "adminServiceImpl")
    private AdminService adminService;

    /**
     * 权限校验步骤：
     * 1. 根据requestURI从配置中(authorizationProperties)查找对应的item。
     * 1.1 如果没有找到，表示该RequestURI允许认证后访问（不需要任何权限）
     * 1.2 如果找到，则和当前用户的权限进行比对，如果没有包含该权限（权限标识），则返回拒绝访问
     *
     * @param authentication
     *         the {@link Supplier} of the {@link Authentication} to check
     * @param object
     *         the {@link T} object to check
     *
     * @return
     */
    @Override
    public AuthorizationDecision check (Supplier <Authentication> authentication, RequestAuthorizationContext object) {
        try {

            Authentication authentication1 = authentication.get();

            // 没有认证，不过一般不会出现这种情况，一般都是AnonymousAuthenticationToken
            if (authentication1 == null || authentication1 instanceof AnonymousAuthenticationToken) {
                log.debug("Authentication is null or Anonymous：{}", authentication1);
                return new AuthorizationDecision(false);
            }

            HttpServletRequest request = object.getRequest();

            String requestURI = request.getRequestURI();

            AuthorizationItem item = this.authorizationProperties.getItemByRequestURI(requestURI);

            // 表示允许访问
            if (item == null) {
                log.debug("允许非授权访问URI：{}", requestURI);
                return new AuthorizationDecision(true);
            }

            log.debug("RequestURI:{}，itemName:{}, itemUrl:{}", requestURI, item.getName(), item.getUrl());

            // 是否有权限
            if (this.adminService.hasPermission(item.getName())) {
                return new AuthorizationDecision(true);
            }

            return new AuthorizationDecision(false);
        } catch (AccessDeniedException ex) {
            return new AuthorizationDecision(false);
        }
    }
}
